package com.ivan.stu.notetool.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;


import com.ivan.stu.notetool.activity.EditNoteActivity;
import com.ivan.stu.notetool.network.api.SafeApiManager;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.concurrent.TimeUnit;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.ForwardingSink;
import okio.ForwardingSource;
import okio.Okio;
import okio.Sink;
import okio.Source;

/**
 * Created by IVAN on 2019/2/26.
 */

public class OkHttpUtils {

    private static final int TIME_OUT = 60;
    private static OkHttpClient sClient;
    private static OkHttpUtils sOkHttpUtils;

    private synchronized static OkHttpClient getOkHttpClientInstance() {
        if (sClient == null) {
            OkHttpClient.Builder builder = new OkHttpClient.Builder()
                    .connectTimeout(TIME_OUT, TimeUnit.SECONDS)
                    .readTimeout(TIME_OUT, TimeUnit.SECONDS)
                    .writeTimeout(TIME_OUT, TimeUnit.SECONDS);
            sClient = builder.build();
        }
        return sClient;
    }

    public static OkHttpUtils getInstance() {
        if (sOkHttpUtils == null) {
            sOkHttpUtils = new OkHttpUtils();
        }
        return sOkHttpUtils;
    }

    public interface ProgressListener {
        void onProgress(long currentBytes, long contentLength, boolean done);
    }

   public void asyncGet(String path, final EditNoteActivity.OkHttpCallBack callback) {


        if (sClient == null){

            OkHttpClient.Builder builder = new OkHttpClient.Builder();

            sClient =   builder.writeTimeout(60,TimeUnit.SECONDS)
                    .readTimeout(60,TimeUnit.SECONDS)
                    .connectTimeout(30,TimeUnit.SECONDS)
                    .addInterceptor(SafeApiManager.getHttpLoggingInterceptor())
                    .build();
        }

        final Request request = new Request.Builder().get()
                .url(path)
                .build();

        sClient.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

                Log.e("error",e.toString());
                callback.onFailure(e.toString());

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                Log.e("success","success");

                //防止内存溢出
                BitmapFactory.Options options = new  BitmapFactory.Options();
                options.inPreferredConfig = Bitmap.Config.RGB_565;
                options.inPurgeable = true;
                options.inInputShareable = true;
                options.inJustDecodeBounds =  false;
                options.inSampleSize =  1;//按比例显示最终的图片

                Bitmap bitmap = BitmapFactory.decodeStream(response.body().byteStream(),null,options);

                //阻塞式方法；
                //  Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                callback.onSuccess(bitmap);

          //      bitmap.recycle();

            }
        });
    }


    public void upImage(final Context context, File file , final EditNoteActivity.HttpCallBack callBack) {

        MultipartBody.Builder builder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("img", file.getName(),
                        RequestBody.create(MediaType.parse("image/png"), file));
        RequestBody requestBody = builder.build();
        Request request = new Request.Builder()
                .url(ConstrantManager.UPLOAD_URL)
                .post(requestBody)
                .build();
        Call call = getOkHttpClientInstance().newCall(request);
        call.enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(final Call call, IOException e) {
                Log.e("response", "onFailure: "+e );
                Global.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                        if (callBack!=null){

                            callBack.onFailure(call.toString());
                        }

                    }
                });
            }
            @Override
            public void onResponse(final Call call, final Response response) throws IOException {

                Global.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            String result = response.body().string();
                            Log.e("response", "成功"+result);

                            if (callBack!=null){

                                callBack.onSuccess(result);

                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
                });
            }
        });

    }

    public Call upload(String url, String fileName, ProgressListener listener, Callback callback) {
        return upload(url, fileName, null, null, listener, callback);
    }

    public Call upload(String url, String fileName, String[] names, String[] values,
                       ProgressListener listener, Callback callback) {
        return upload(url, null, null, fileName, names, values,
                listener, callback);
    }

    public Call upload(String url,
                       String[] headerNames, String[] headerValues, String fileName,
                       ProgressListener listener, Callback callback) {
        return upload(url, headerNames, headerValues, fileName, null, null,
                listener, callback);
    }

    /**
     * 只上传文件
     * 根据url，发送异步Post请求(带进度)
     *
     * @param url          提交到服务器的地址
     * @param headerNames  请求头的名称数组
     * @param headerValues 请求头的值数组
     * @param fileName     完整的上传的文件的路径名
     * @param names        参数名
     * @param values       参数值
     * @param listener     上传进度的监听器
     * @param callback     OkHttp的回调接口
     * @return 返回Call
     */
    public Call upload(String url,
                       String[] headerNames, String[] headerValues,
                       String fileName, String[] names, String[] values,
                       ProgressListener listener, Callback callback) {
        Request request = getRequest(url, headerNames, headerValues,
                fileName, names, values, listener);
        Call call = getOkHttpClientInstance().newCall(request);
        call.enqueue(callback);
        return call;
    }

    private Request getRequest(String url,
                               String[] headerNames, String[] headerValues,
                               String filePath, String[] names, String[] values,
                               ProgressListener listener) {
        // 构造请求
        Request.Builder builder = new Request.Builder()
                .url(url)
                .post(new ProgressRequestBody(getRequestBody(filePath, names, values), listener));

        // 添加头部
        addHeader(builder, headerNames, headerValues);

        return builder.build();
    }

    /**
     * 通过上传的文件的完整路径生成RequestBody
     *
     * @param filePath 完整的文件路径
     * @param names    参数名
     * @param values   参数值
     * @return
     */
    private RequestBody getRequestBody(String filePath, String[] names, String[] values) {
        // 创建MultipartBody.Builder，用于添加请求
        MultipartBody.Builder builder = new MultipartBody.Builder();
        builder.setType(MultipartBody.FORM);

        File file = new File(filePath); // 生成文件
        // 根据文件的后缀名，获得文件类型
        String fileType = getMimeType(file.getName());
        builder.addFormDataPart( // 给Builder添加上传的文件
                "FileUpload", // 请求的名字
                file.getName(), // 文件的名字，服务器端用来解析的
                RequestBody.create(MediaType.parse(fileType), file) // 创建RequestBody，把上传的文件放入
        );

        // 加参数
        if (hasParams(names, values)) {
            for (int i = 0; i < names.length; i++) {
                builder.addFormDataPart(names[i], values[i]);
            }
        }

        return builder.build(); // 根据Builder创建请求
    }

    /**
     * 获取文件MimeType
     *
     * @param fileName 文件名
     * @return
     */
    private String getMimeType(String fileName) {
        FileNameMap filenameMap = URLConnection.getFileNameMap();
        String contentType = filenameMap.getContentTypeFor(fileName);
        if (contentType == null) {
            contentType = "application/octet-stream"; //* exe,所有的可执行程序
        }
        return contentType;
    }

    /**
     * 包装的请求体，处理进度
     */
    public static class ProgressRequestBody extends RequestBody {
        // 实际的待包装请求体
        private final RequestBody requestBody;
        // 进度回调接口
        private final ProgressListener progressListener;
        // 包装完成的BufferedSink
        private BufferedSink bufferedSink;

        /**
         * 构造函数，赋值
         *
         * @param requestBody      待包装的请求体
         * @param progressListener 回调接口
         */
        public ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) {
            this.requestBody = requestBody;
            this.progressListener = progressListener;
        }

        /**
         * 重写调用实际的响应体的contentType
         *
         * @return MediaType
         */
        @Override
        public MediaType contentType() {
            return requestBody.contentType();
        }

        /**
         * 重写调用实际的响应体的contentLength
         *
         * @return contentLength
         * @throws IOException 异常
         */
        @Override
        public long contentLength() throws IOException {
            return requestBody.contentLength();
        }

        /**
         * 重写进行写入
         *
         * @param sink BufferedSink
         * @throws IOException 异常
         */
        @Override
        public void writeTo(BufferedSink sink) throws IOException {
            if (bufferedSink == null) {
                // 包装
                bufferedSink = Okio.buffer(sink(sink));
            }
            // 写入
            requestBody.writeTo(bufferedSink);
            // 必须调用flush，否则最后一部分数据可能不会被写入
            bufferedSink.flush();
        }

        /**
         * 写入，回调进度接口
         *
         * @param sink Sink
         * @return Sink
         */
        private Sink sink(Sink sink) {
            return new ForwardingSink(sink) {
                // 当前写入字节数
                long bytesWritten = 0L;
                // 总字节长度，避免多次调用contentLength()方法
                long contentLength = 0L;

                @Override
                public void write(Buffer source, long byteCount) throws IOException {
                    super.write(source, byteCount);
                    if (contentLength == 0) {
                        // 获得contentLength的值，后续不再调用
                        contentLength = contentLength();
                    }
                    // 增加当前写入的字节数
                    bytesWritten += byteCount;
                    // 回调
                    if (progressListener != null) {
                        progressListener.onProgress(
                                bytesWritten,
                                contentLength,
                                bytesWritten == contentLength);
                    }
                }
            };
        }
    }

    public Call download(String downloadUrl, ProgressListener listener, Callback callback) {
        return download(downloadUrl, null, null, listener, callback);
    }

    /**
     * 文件下载（带进度）
     *
     * @param downloadUrl 文件的下载地址
     * @param names       参数名
     * @param values      参数值
     * @param listener    下载进度的监听器
     * @param callback    下载回调
     * @return 返回Call
     */
    public Call download(String downloadUrl, String[] names, String[] values,
                         final ProgressListener listener, Callback callback) {
        Interceptor interceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                // 拦截
                Response originalResponse = chain.proceed(chain.request());
                // 包装响应体并返回
                return originalResponse
                        .newBuilder()
                        .body(new ProgressResponseBody(originalResponse.body(), listener))
                        .build();
            }
        };
        OkHttpClient client = getOkHttpClientInstance()
                .newBuilder().addInterceptor(interceptor).build();

        // 加参数（支持GET方式）
        if (hasParams(names, values)) {
            StringBuilder sb = new StringBuilder(downloadUrl);
            sb.append("?");
            for (int i = 0; i < names.length; i++) {
                sb.append(names[i]);
                sb.append("=");
                sb.append(values[i]);
                if (i != names.length - 1) {
                    sb.append("&");
                }
            }
            downloadUrl = sb.toString();
        }

        Call call = client.newCall(getRequest(downloadUrl));
        call.enqueue(callback);
        return call;
    }

    /**
     * 通过下载的URL地址构建request实例
     *
     * @param downloadUrl 文件下载的地址
     * @return
     */
    private Request getRequest(String downloadUrl) {
        Request.Builder builder = new Request.Builder()
                .url(downloadUrl)
                .tag(downloadUrl)
                .addHeader("Accept-Encoding", "identity");// 解决无法获取文件长度问题
        return builder.build();
    }

    /**
     * 通过响应保存文件
     *
     * @param response 响应对象
     * @param saveFile 下载后的文件的保存路径
     * @throws IOException
     */
    public static void saveDownloadFile(Response response, File saveFile) throws IOException {
        InputStream inputStream = getInputStreamFromResponse(response);
        BufferedInputStream bis = new BufferedInputStream(inputStream);
        FileOutputStream fos = new FileOutputStream(saveFile);
        byte[] data = new byte[10 * 1024];
        int len;
        while ((len = bis.read(data)) != -1) {
            fos.write(data, 0, len);
        }
        fos.flush();
        fos.close();
        bis.close();
    }

    /**
     * 根据响应获得输入流
     *
     * @param response
     * @return
     * @throws IOException
     */
    private static InputStream getInputStreamFromResponse(Response response) throws IOException {
        if (response != null && response.isSuccessful()) {
            ResponseBody responseBody = response.body();
            if (responseBody != null) {
                return responseBody.byteStream();
            }
        }
        return null;
    }

    /**
     * 包装的响体，处理进度
     */
    public static class ProgressResponseBody extends ResponseBody {
        // 实际的待包装响应体
        private final ResponseBody responseBody;
        // 进度回调接口
        private final ProgressListener progressListener;
        // 包装完成的BufferedSource
        private BufferedSource bufferedSource;

        /**
         * 构造函数，赋值
         *
         * @param responseBody     待包装的响应体
         * @param progressListener 回调接口
         */
        public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
            this.responseBody = responseBody;
            this.progressListener = progressListener;
        }


        /**
         * 重写调用实际的响应体的contentType
         *
         * @return MediaType
         */
        @Override
        public MediaType contentType() {
            return responseBody.contentType();
        }

        /**
         * 重写调用实际的响应体的contentLength
         *
         * @return contentLength
         * @throws IOException 异常
         */
        @Override
        public long contentLength() {
            return responseBody.contentLength();
        }

        /**
         * 重写进行包装source
         *
         * @return BufferedSource
         * @throws IOException 异常
         */
        @Override
        public BufferedSource source() {
            if (bufferedSource == null) {
                // 包装
                bufferedSource = Okio.buffer(source(responseBody.source()));
            }
            return bufferedSource;
        }

        /**
         * 读取，回调进度接口
         *
         * @param source Source
         * @return Source
         */
        private Source source(Source source) {

            return new ForwardingSource(source) {
                // 当前读取字节数
                long totalBytesRead = 0L;

                @Override
                public long read(Buffer sink, long byteCount) throws IOException {
                    long bytesRead = super.read(sink, byteCount);
                    // 增加当前读取的字节数，如果读取完成了bytesRead会返回-1
                    totalBytesRead += bytesRead != -1 ? bytesRead : 0;
                    // 回调，如果contentLength()不知道长度，会返回-1
                    if (progressListener != null) {
                        progressListener.onProgress(totalBytesRead, responseBody.contentLength(),
                                bytesRead == -1);
                    }
                    return bytesRead;
                }
            };
        }
    }

    /**
     * 请求的回调接口
     */
    public interface HttpResponseCallback {
        void onSucceed(String data);

        void onFailed(String msg);
    }

    public Call doGet(String baseUrl,
                      String[] paramNames, String[] paramValues,
                      HttpResponseCallback callback) {
        return doGet(baseUrl, null, null, paramNames, paramValues, callback);
    }

    /**
     * 发送GET请求
     *
     * @param baseUrl      请求地址
     * @param headerNames  请求头的名称数组
     * @param headerValues 请求头的值数组
     * @param paramNames   参数名数组
     * @param paramValues  参数数组
     * @param callback     结果回调
     * @return 返回Call
     */
    public Call doGet(String baseUrl,
                      String[] headerNames, String[] headerValues,
                      String[] paramNames, String[] paramValues,
                      HttpResponseCallback callback) {
        // 拼接参数
        if (hasParams(paramNames, paramValues)) {
            baseUrl = buildParam(baseUrl, paramNames, paramValues);
        }
        // 构造请求
        Request.Builder builder = new Request.Builder().url(baseUrl);

        // 添加头部
        addHeader(builder, headerNames, headerValues);

        Request request = builder.build();
        // 提交请求
        return postRequest(request, callback);
    }

    public Call doPost(String baseUrl,
                       String[] paramNames, String[] paramValues,
                       HttpResponseCallback callback) {
        return doPost(baseUrl, null, null, paramNames, paramValues, callback);
    }

    /**
     * 发送POST请求
     *
     * @param baseUrl      请求地址
     * @param headerNames  请求头的名称数组
     * @param headerValues 请求头的值数组
     * @param paramNames   参数名数组
     * @param paramValues  参数数组
     * @param callback     结果回调
     * @return 返回Call
     */
    public Call doPost(String baseUrl,
                       String[] headerNames, String[] headerValues,
                       String[] paramNames, String[] paramValues,
                       HttpResponseCallback callback) {
        // 构造表单参数
        FormBody.Builder formBuilder = new FormBody.Builder();
        if (hasParams(paramNames, paramValues)) {
            for (int i = 0; i < paramNames.length; i++) {
                formBuilder.add(paramNames[i], paramValues[i]);
            }
        }
        RequestBody formBody = formBuilder.build();
        // 构造请求
        Request.Builder builder = new Request.Builder()
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .url(baseUrl)
                .post(formBody);

        // 添加头部
        addHeader(builder, headerNames, headerValues);

        Request request = builder.build();
        // 提交请求
        return postRequest(request, callback);
    }

    public Call doPost(String baseUrl,
                       String[] paramNames, String[] paramValues,
                       String json,
                       HttpResponseCallback callback) {
        return doPost(baseUrl, null, null, paramNames, paramValues, json, callback);
    }

    /**
     * 发送POST请求
     *
     * @param baseUrl      请求地址
     * @param headerNames  请求头的名称数组
     * @param headerValues 请求头的值数组
     * @param paramNames   参数名数组
     * @param paramValues  参数数组
     * @param json         JSON字符串
     * @param callback     结果回调
     * @return 返回Call
     */
    public Call doPost(String baseUrl,
                       String[] headerNames, String[] headerValues,
                       String[] paramNames, String[] paramValues,
                       String json,
                       HttpResponseCallback callback) {
        // 拼接参数
        if (hasParams(paramNames, paramValues)) {
            baseUrl = buildParam(baseUrl, paramNames, paramValues);
        }
        // 构造表单参数
        RequestBody formBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), json);
        // 构造请求
        Request.Builder builder = new Request.Builder()
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .url(baseUrl)
                .post(formBody);

        // 添加头部
        addHeader(builder, headerNames, headerValues);

        Request request = builder.build();
        // 提交请求
        return postRequest(request, callback);
    }

    public boolean hasParams(String[] paramNames, String[] paramValues) {
        return paramNames != null && paramValues != null
                && paramNames.length == paramValues.length
                && paramNames.length > 0;
    }

    private String buildParam(String baseUrl, String[] paramNames, String[] paramValues) {
        StringBuilder sb = new StringBuilder(baseUrl);
        sb.append("?");
        for (int i = 0; i < paramNames.length; i++) {
            sb.append(paramNames[i]);
            sb.append("=");
            sb.append(paramValues[i]);
            if (i != paramNames.length - 1) {
                sb.append("&");
            }
        }
        return sb.toString();
    }

    private void addHeader(Request.Builder builder, String[] headerNames, String[] headerValues) {
        if (headerNames != null && headerValues != null
                && headerNames.length == headerValues.length) {
            for (int i = 0; i < headerNames.length; i++) {
                builder.addHeader(headerNames[i], headerValues[i]);
            }
        }
    }

    private Call postRequest(Request request, final HttpResponseCallback callback) {
        // 生成一个任务
        Call call = getOkHttpClientInstance().newCall(request);
        // 将call加入调度队列，等待任务执行
        call.enqueue(new Callback() {// 注意以下方法的调用仍然在子线程
            @Override
            public void onResponse(Call call, Response response)
                    throws IOException {
                if (callback != null) {
                    int code = response.code();
                    if (code == 200) {
                        String data = response.body().string();
                        callback.onSucceed(data);
                    } else {
                        callback.onFailed("错误的HTTP状态码： " + code);
                    }
                }
            }

            @Override
            public void onFailure(Call call, IOException e) {
                if (callback != null) {
                    String msg = e.getMessage();
                    if (msg == null || msg.length() == 0) {
                        msg = "请求发生错误，请重试";
                    } else {
                        msg = msg.trim();
                    }
                    callback.onFailed(msg);
                }
                e.printStackTrace();
            }
        });
        return call;
    }
}
